import { functions } from "lodash"

// 柯理化
function curry(fn){
    const fnLen = fn.length

    return function _curry (...args1) {
        if(args1.length < fnLen){
            return function (...args2){
                const params = args1.concat(args2)
                return _curry(...params)
            }
        }

        return fn.call(this, ...args1)
    }
}

function func(a, b, c, d){
    console.log(a + b + c + d)
}

const fn = curry(func)

fn(1)(2,3,4)
fn(1)(2)(3)(4)
fn(1,2,3,4)
fn(1)(2,3)(4)



// Function.prototype.myBind = function(obj, ...args){
//     if(typeof obj ==="undefined" ){
//         return this
//     }
//     obj.func = this

//     return function(...args2){
//         let ops = args.concat(args2)
//         obj.func(...ops)
//     }
// }


Function.prototype.myCall = function (obj, ...args) {
    // 这种写法包含obj为null
    let o = Object.create(obj)
    o.func = this
    return o.func(...args)
}


// 考虑 obj 为 null 的情况
Function.prototype.myApply = function (obj = window, args) {
    obj.func = this
    return obj.func(...args)
}



var obj = {
    name: "张三"
}

var b = {
    name: "李四",
    func: function(a, b){
        console.log(this.name + a + b)
    }
}

b.func("1", "2")

// const func1 = bindFunc.myBind(obj, "a")
// b.func("b")
b.func.myCall(obj, "a", "b")
b.func.myApply(obj, ["a", "b"])





function funca(a){
    return a + 1
}

function funcb(b){
    return b + 2
}


console.log(funca(funcb(1)))

const cFunc = myCompose(funca, funcb)
console.log(cFunc(1))

// 组合函数
function myCompose(...fns){
    return function(value){
        return fns.reduce((sum, fn)=>{
            return fn(sum)
        },value)
    }
}


// 寄生组合继承
function Parent (age) {
    this.age = age
    this.show = function (){
        console.log("name, age: ", this.name + this.age)
    }
}

Parent.prototype.name = "Parent"

function Son (...args) {
    Parent.call(this, ...args)
}

Son.prototype = Object.create(Parent.prototype)
Son.prototype.constuctor = Son

const son = new Son(20)
son.show()


// myPromise

const PENDDING = "pendding"
const SUCCESS = "success"
const FAILED = "failed"

class myPromise {
    constructor(fn){
        this.status = PENDDING
        this.value = null
        this.error = null
        this.successCallback = []
        this.failedCallback = []

        fn && fn(this.resolve, this.reject)
    }

    resolve = (value) => {
        if(this.status!==PENDDING){
            return
        }
        this.status = SUCCESS
        this.value = value
        while(this.successCallback.length){
            this.successCallback.shift()()
        }
    }

    reject = (error) => {
        if(this.status!==PENDDING){
            return
        }
        this.status = FAILED
        this.error = error
        while(this.failedCallback.length){
            this.failedCallback.shift()()
        }
    }

    then(successCallback, failedCallback) {
        if(this.status === SUCCESS){
            successCallback && successCallback(this.value)
        }

        if(this.status === FAILED){
            failedCallback && failedCallback(this.error)
        }

        if(this.status === PENDDING){
            this.successCallback.push(()=>{
                successCallback && successCallback(this.value)
            })
            this.failedCallback.push(()=>{
                failedCallback && failedCallback(this.error)
            })
        }
    }


    static resolve (value) {
        if(value instanceof Promise || value instanceof myPromise){
            return value
        }

        return new myPromise((resolve, reject)=>{
            resolve(value)
        })
    }

    static all(promiseArr){

        return new myPromise((resolve, reject)=>{
            let result = []

            const addResult = (value)=>{
                result.push(value)
                if(result.length === promiseArr.length){
                    resolve(result)
                }
            }
    
            promiseArr.map(p=>{
                myPromise.resolve(p).then((value)=>{
                    addResult(value)
                }, reject)
            })
        })
    }


    static race(promiseArr) {
        return new myPromise((resolve, reject)=>{
            promiseArr.map(p=>{
                myPromise.resolve(p).then((value)=>{
                    resolve(value)
                }, reject)
            })

        })
    }
}


// let p = new myPromise((resolve, reject) => {
//     setTimeout(()=>{
//         resolve("myPromise p")
//     }, 1000)
// })


// p.then(value => {
//     console.log("then1: ", value)
// }, error => {
//     console.log(error)
// })


// p.then(value => {
//     console.log("then2: ", value)
// }, error => {
//     console.log(error)
// })

// myPromise.resolve(p).then(value=>{
//     console.log("resolve, ", value)
// })



// let p2 = new myPromise((resolve, reject) => {
//     setTimeout(()=>{
//         resolve("myPromise p2")
//     }, 2000)
// })


// myPromise.all([p, p2]).then(arr => console.log("all", arr))
// myPromise.race([p, p2]).then(value => console.log("race:", value))




// // 并行限制的promise【遗留问题】
// function priomiseTread (limits, funcs) {



// }

// function promiseTask (p) {
//     return function () {
//         return new myPromise((resolve, reject) => {
//             setTimeout(()=>{
//                 resolve("myPromise" + p)
//             }, 2000)
//         })
//     }
// }

// let asyncFunctions = [
//     promiseTask("p3"),
//     promiseTask("p4"),
//     promiseTask("p5"),
//     promiseTask("p6")
// ]



// let pt = priomiseTread(2, asyncFunctions)

// pt.then(value=>{
//     console.log("priomiseTread:", value)
// })






function TestConstuct(name){
    this.name = name
}
TestConstuct.prototype.age = 18


// 创建一个空对象 {}
// 将对象的__proto__指向构造函数的prototype
// 正确设置对象的this
// 返回this
function myNew(construct, ...args) {
    let obj = {}
    obj.__proto__ = construct.prototype
    // ===> 等同于：
    // let obj = Object.create(construct.prototype)

    // 执行构造函数
    construct.call(obj, ...args)

    return obj
}


// const myobj = myNew(TestConstuct, "nnns")
// console.log("myobj:", myobj)

// deepClone
function MakeObj () {
    this.number = 10
    this.string = "string"
    this.boolean = true
    this.null = null
    this.array = [1,2,3,4,5],
    this.subObj = {
        subArray1: [1,2,3,4,5],
        subArray2: [
            {name: "zhangsan"},
            {name: "lisi"}
        ]
    }
    this.date = new Date()
    this.reg = /test/gi
    this.func = function() {
        console.log("this:", this)
        console.log("string:", this.string)
    }
}
const obj1 = new MakeObj()

obj1.__proto__.p1 = "p1 text"
obj1.func.__proto__.p2 = "p2 __proto__"
obj1.func.prototype.p2 = "p2 prototype"
obj1.array.__proto__.p3 = "p3 text"
// obj1.subObj.circle = obj1 // 循环引用

// 测试代码
const deepObj = deepClone(obj1)
console.log("deepObj:", deepObj)

function deepClone(obj) {

    let chain = {}


    const _deep = (obj) => {
        const type = Object.prototype.toString.call(obj)
        // 数组
        if(type === "[object Array]"){
            return obj.map(item => _deep(item))
        }

        // 对象
        if(type === "[object Object]"){
            let newObject = Object.create(obj.prototype)
            for(let property in obj){
                if(obj.hasOwnProperty(property)){
                    newObject[property] = _deep(obj[property])
                }
            }

            return newObject
        }

        if(type === "[object Function]"){
            let newFunction = function(...args){
                obj.call(this, ...args)
            }
            newFunction.prototype = obj.prototype
            return newFunction
        }

        return obj
    }


    return _deep(obj)

}

function same() {
    console.log("same function")
}





// 数组去重
const repeatArray = [
    1,
    1,
    undefined,
    undefined,
    null,
    false,
    true,
    true,
    NaN,
    NaN,
    {},
    {},
    function(){
        console.log("function")
    },
    function(){
        console.log("function")
    },
    same,
    same,
    new Date(),
    /^reg/gi,
    /^reg/gi
]

function removeRepeat (arr) {
    // // 方法一：
    // let newArray = Array.from(new Set(arr))

    // 方法二：
    let newArray = []
    arr.map(item => {
        if(!newArray.includes(item)){
            newArray.push(item)
        }
    })

    console.log("newArray:", newArray)
}

removeRepeat(repeatArray)







// Promise 并行请求
var urls = [
    'http://jsonplaceholder.typicode.com/posts/1',
    'http://jsonplaceholder.typicode.com/posts/2', 
    'http://jsonplaceholder.typicode.com/posts/3', 
    'http://jsonplaceholder.typicode.com/posts/4',
    'http://jsonplaceholder.typicode.com/posts/5', 
    'http://jsonplaceholder.typicode.com/posts/6', 
    'http://jsonplaceholder.typicode.com/posts/7', 
    'http://jsonplaceholder.typicode.com/posts/8',
    'http://jsonplaceholder.typicode.com/posts/9', 
    'http://jsonplaceholder.typicode.com/posts/10'
]

// function loadDate (url, wait = 1000) {
//     return new Promise((resolve, reject) => {
//         console.log("loadDate：", url)
//         setTimeout(() => {
//             resolve(url)
//         }, wait)
//     })
// }


function loadDate (url, wait = 1000) {
    return () => {
        return new Promise((resolve, reject) => {
            console.log("loadDate：", url)
            setTimeout(() => {
                resolve(url)
            }, wait)
        })
    }
}


let taskQueue = []

function addTask(promise) {
    taskQueue.push(promise)
}

function excuteTask(limits) {
    let i = 0 
    let promises = []

    const shiftTask = (resolve, reject) => {
        if(taskQueue.length < 1){
            Promise.all(promises).then(resolve, reject)
            return
        }

        const p = taskQueue.shift()()
        promises.push(p)
        p.then(data => {
            // console.log("resolve：")
            shiftTask(resolve, reject)
        })
    }

    return new Promise((resolve, reject) => {
        while(i < limits){
            shiftTask(resolve, reject)
            i++
        }
    })
}



addTask(loadDate(urls[0], 1000))
addTask(loadDate(urls[1], 1000))
addTask(loadDate(urls[2], 3000))
addTask(loadDate(urls[3], 3000))
addTask(loadDate(urls[4], 1000))
addTask(loadDate(urls[5], 1000))



excuteTask(2).then((arr)=>{
    console.log("arr:", arr)
})













// 该方法比较绕！！不太好！！
// 控制发送线程，并行发送
function limitSend(urls, load, limits) {
    let pAll = []
    let pLoading = [] 
    let urlsLimits = urls.slice(0, limits)
    let urlsLeft = urls.slice(limits, urls.length)

    const send = (url, index) => {
        let promise = load(url)

        pAll.push(promise)

        return promise.then(()=>{
            return index // 用于在发送新的请求时使用
        })
    }

    // 并行发送
    pLoading = urlsLimits.map((url, index) => send(url, index))

    
    return urlsLeft.reduce((fast, url, currentIndex)=>{
        return fast.then(index => {
            console.log("resolve：", index)
            
            pLoading[index] = send(url, index) // 替换掉最先完成的请求替换掉

            return Promise.race(pLoading)
        })

    }, Promise.race(pLoading)).then(()=>{
        
        return Promise.all(pAll)
    })

}


// // 确定发送方法的调用：
// limitSend(urls, loadDate, 3).then(resolveArray => {
//     console.log('所有url数据请求成功：', resolveArray);
// }).catch(err => {
//     console.error(err);
// })



// // 利用数组的 reduce 方法来以队列的形式执行
// return sequence.reduce((last, url, currentIndex) => {
//     return last.then(() => {
//         // 返回最快改变状态的 Promise
//         return Promise.race(promises)
//     }).catch(err => {
//         // 这里的 catch 不仅用来捕获前面 then 方法抛出的错误
//         // 更重要的是防止中断整个链式调用
//         console.error(err)
//     }).then((res) => {
//         // 用新的 Promise 替换掉最快改变状态的 Promise
//         promises[res] = handler(sequence[currentIndex]).then(() => { return res });
//     })
// }, Promise.resolve()).then(() => {
//     return Promise.all(promises)
// })



